

package net.lastcoder.ifserver.nio;

import net.lastcoder.ifserver.Constants;
import net.lastcoder.ifserver.Server;
import net.lastcoder.ifserver.module.ModuleManager;
import net.lastcoder.ifserver.module.ModuleNames;
import net.lastcoder.ifserver.packet.Packet;
import net.lastcoder.ifserver.packet.PacketParser;
import net.lastcoder.ifserver.route.PacketRouter;
import net.lastcoder.ifserver.session.ClientSession;
import net.lastcoder.ifserver.session.SessionHandler;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

public abstract class ConnectionHandler extends IoHandlerAdapter {

	private static final Logger Log = LoggerFactory.getLogger(ConnectionHandler.class);

    protected static final String CONNECTION = "CONNECTION";
    protected static final String PACKET_PARSER = "PARSER";

   // private ConnectPrivacy connectPrivacy;
    private PacketRouter packetRouter;
    private SessionHandler sessionHandler;
    public ConnectionHandler() {
        this.packetRouter = Server.getInstance().getPacketRouter();
        sessionHandler = (SessionHandler) ModuleManager.getInstance()
                .getModule(ModuleNames.SessionHandler);
       // connectPrivacy = ConnectPrivacy.getInstance();
    }

    @Override
	public void sessionOpened(IoSession session) throws Exception {
        boolean ban = false;
        final SocketAddress remoteAddress = session.getRemoteAddress();
        if (remoteAddress == null) {
            ban = true;
        } else {
            final InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
            final InetAddress inetAddress = socketAddress.getAddress();
//            if (connectPrivacy.isBan(inetAddress.getHostAddress())) {
//                ban = true;
//            }
        }

        if (ban) {
            session.close(true);
            return;
        }

        final NIOConnection connection = createNIOConnection(session);
        final PacketParser parser = new LightPacketParser();
        session.setAttribute(PACKET_PARSER, parser);
        session.setAttribute(CONNECTION, connection);
        final int idleTime = getMaxIdleTime() / 2;
        if (idleTime > 0) {
            session.getConfig().setIdleTime(IdleStatus.READER_IDLE, idleTime);
        }
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        final Connection connection = (Connection) session.getAttribute(CONNECTION);
        if (connection != null) {
            connection.close();
        }
    }

    @Override
	public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        if (session.getIdleCount(status) > 1) {
            // Get the connection for this session
            final Connection connection = (Connection) session.getAttribute(CONNECTION);
            if (connection != null) {
                // Close idle connection
                if (Log.isDebugEnabled()) {
                    Log.debug("ConnectionHandler: Closing connection that has been idle: " + connection);
                }
                connection.close();
            }
        }
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        if (!cause.getMessage().contains("reset by peer")) {
            Log.warn("Closing connection due to exception + " + cause.getMessage() 
                    + " in session: " + session, cause);
        }

        try {
            final Connection connection = (Connection) session.getAttribute(CONNECTION);
            byte[] error = new byte[1];
            error[0] = '\0';
            connection.deliverRawData(error);
        } finally {
            final Connection connection = (Connection) session.getAttribute(CONNECTION);
            if (connection != null) {
                connection.close();
            }
        }
    }

    @Override
	public void messageReceived(IoSession session, Object message) throws Exception {
        Packet packet = (Packet) message;
        Log.info("Received : " + packet.toString());
        // 这里解析包
        if (packet.getToAddress() == null) {
            packet.setToAddress(Server.getInstance().getServerId());
        }

        Connection conn = (Connection) session.getAttribute(CONNECTION);
        if (packet.getPacketType() == Constants.PACKET_TYPE_ZERO) {
            // 收到一个为0的包
            conn.close();
        } else if (packet.getPacketType() == Constants.PACKET_TYPE_SESSION_OPEN
                || packet.getPacketType() == Constants.PACKET_TYPE_SESSION_CLOSE) {
            // 递交处理
            try {
                sessionHandler.handle(conn, packet);
            } catch (Exception e) {
                // TODO
                Log.error(e.getMessage(), e);
                conn.close();
            }
        } else {
            ClientSession clientSession = (ClientSession) conn.getOwner();
            if (clientSession != null) {
                clientSession.incrementClientPacketCount();
            }
            packetRouter.route(clientSession, packet, true);
        }
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
    }

    abstract NIOConnection createNIOConnection(IoSession session);

    /**
     * Returns the max number of seconds a connection can be idle (both ways) before
     * being closed.<p>
     *
     * @return the max number of seconds a connection can be idle.
     */
    abstract int getMaxIdleTime();

}